﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Xml.Linq;
using NET.Tools.OS;
using Puzzler.Common;
using Puzzler.Common.Exceptions;
using Puzzler.Plugin.Utils;

namespace Puzzler.Plugin.Interface
{
    public abstract class GridPuzzleModel<TInfo> : PuzzleModel where TInfo : GridCellInfo
    {
        public static readonly DependencyProperty SelectedCellProperty = DependencyProperty.Register(
            "SelectedCell", typeof (Point), typeof (GridPuzzleModel<TInfo>), new PropertyMetadata(new Point()));

        public static readonly DependencyProperty GridProperty = DependencyProperty.Register(
            "Grid", typeof (TInfo[][]), typeof (GridPuzzleModel<TInfo>), new PropertyMetadata(null));

        private readonly int cellWidth, cellHeight;

        /// <summary>
        /// <b>Overwrite constructor: Need constructor with: PuzzleData</b>
        /// Set cell width and cell height values (only rendering)
        /// </summary>
        /// <param name="cellWidth"></param>
        /// <param name="cellHeight"></param>
        protected GridPuzzleModel(int cellWidth, int cellHeight)
        {
            this.cellWidth = cellWidth;
            this.cellHeight = cellHeight;
        }

        public override void Update(PuzzleData data)
        {
            base.Update(data);

            Grid = PuzzlerPluginUtilsService.ConvertToArray2D<TInfo>(data.GridRects);
            for (int i = 0; i < GridColumnCount; i++)
            {
                for (int j = 0; j < GridRowCount; j++)
                {
                    TInfo info = Grid[i][j];
                    if (info != null)
                    {
                        info.PropertyChanged += (s, e) => OnPropertyChanged(GridProperty.Name);
                    }
                }
            }
        }

        internal TInfo[][] Grid
        {
            get { return (TInfo[][])GetValue(GridProperty); }
            set { SetValue(GridProperty, value); }
        }

        public int CellWidth
        {
            get { return cellWidth; }
        }

        public int CellHeight
        {
            get { return cellHeight; }
        }

        public TInfo SelectedCellInfo
        {
            get { return Grid[(int) SelectedCell.X][(int) SelectedCell.Y]; }
        }

        public int SelectedColumn
        {
            get { return (int) SelectedCell.X; }
        }

        public int SelectedRow
        {
            get { return (int) SelectedCell.Y; }
        }

        public Point SelectedCell
        {
            get { return (Point) GetValue(SelectedCellProperty); }
            set { SetValue(SelectedCellProperty, value); }
        }

        public int GridRowCount
        {
            get { return Grid[0].Length; }
        }

        public int GridColumnCount
        {
            get { return Grid.Length; }
        }

        public TInfo this[int column, int row]
        {
            get
            {
                if (column < 0 || column >= Grid.Length ||
                    row < 0 || row >= Grid[0].Length)
                    throw new IndexOutOfRangeException("Column or row out of range!");

                return Grid[column][row];
            }
        }

        public bool GoSelectionRight()
        {
            if (SelectedCell.X + 1 < Grid.Length &&
                Grid[(int)(SelectedCell.X + 1)][(int)SelectedCell.Y] != null)
            {
                SelectedCell = new Point(SelectedCell.X + 1, SelectedCell.Y);
                return true;
            }

            return false;
        }

        public bool GoSelectionLeft()
        {
            if (SelectedCell.X - 1 >= 0 &&
                Grid[(int)(SelectedCell.X - 1)][(int)SelectedCell.Y] != null)
            {
                SelectedCell = new Point(SelectedCell.X - 1, SelectedCell.Y);
                return true;
            }

            return false;
        }

        public bool GoSelectionTop()
        {
            if (SelectedCell.Y - 1 >= 0 &&
                Grid[(int)SelectedCell.X][(int)(SelectedCell.Y - 1)] != null)
            {
                SelectedCell = new Point(SelectedCell.X, SelectedCell.Y - 1);
                return true;
            }

            return false;
        }

        public bool GoSelectionBottom()
        {
            if (SelectedCell.Y + 1 < Grid[0].Length &&
                Grid[(int)SelectedCell.X][(int)(SelectedCell.Y + 1)] != null)
            {
                SelectedCell = new Point(SelectedCell.X, SelectedCell.Y + 1);
                return true;
            }

            return false;
        }
    }
}
